//
//  GDDBDatabaseManager.m
//  Gunterapple
//
//  Created by 热带雨林 on 2021/2/11.
//  Copyright © 2021 热带雨林. All rights reserved.
//

#import "GDDBDatabaseManager.h"

typedef enum :NSInteger{
    SQLStringTypeCreate,
    SQLStringTypeInsert,
    SQLStringTypeUPdate,
    SQLStringTypeGetTheLastData,
    SQLStringTypeGetSeaverlDate,
    SQLStringTypeGetAllData,
}SQLStringType;


static GDDBDatabaseManager *_intence;

static NSString * const AUTOINCREMENT_FILEID = @"chatID";

@interface GDDBDatabaseManager ()

@property (nonatomic, strong) FMDatabaseQueue *databaseQueue;

@property (nonatomic, strong) NSString *dbFilePath;//数据库路径
@property (nonatomic, strong) NSString *dbTableName;//数据库表名称

@end

@implementation GDDBDatabaseManager

/**
 实例化
 */
+ (instancetype)shareDBManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _intence = [[self alloc]init];
    });
    return _intence;
}

- (instancetype)init{
    if(self =  [super init]){
        
    }
    return self;
}

/**
 创建表 && 插入数据
 */
- (BOOL)insertDataWithModel:(id)model withFileName:(NSString *)FileName{
    [self getFilePathWithFileName:FileName];

    BOOL __block result;
    self.databaseQueue = [FMDatabaseQueue databaseQueueWithPath:_dbFilePath];
    [self.databaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
        if([db open]){
            //为数据库设置缓存，提高查找效率
            [db setShouldCacheStatements:YES];
            //数据库中表不存在
            if(![db tableExists:_dbTableName]){
                //开始创建这张表
                if([db executeUpdate:[self concatenateSQLStringWithType:SQLStringTypeCreate withModel:model withDataCount:0]]){
                    //表创建之后，开始插入数据
                    if([db executeUpdate:[self concatenateSQLStringWithType:SQLStringTypeInsert withModel:model withDataCount:0]]){
                        result = YES;
                        NSLog(@"表创建成功！");
                    }else{
                        result = NO;
                        NSLog(@"表创建失败！");
                    }
                }
            }else{//表已经存在
                if([db executeUpdate:[self concatenateSQLStringWithType:SQLStringTypeInsert withModel:model withDataCount:0]]){
                    result = YES;
                    NSLog(@"成功");
                }else{
                    result = NO;
                    NSLog(@"失败");
                }
            }
        }
        [db close];
    }];
    return result;
}
/**
 获得数据库中所有数据
 */
- (NSArray *)getAllDataWithModelClass:(Class)kclass withFileName:(NSString *)fileName{
    [self getFilePathWithFileName:fileName];
    NSMutableArray *result = [[NSMutableArray alloc]init];
    self.databaseQueue = [FMDatabaseQueue databaseQueueWithPath:_dbFilePath];
    [self.databaseQueue inDatabase:^(FMDatabase * _Nonnull db) {
        if([db open]){
            //表不存在
            if(![db tableExists:_dbTableName]){
                NSLog(@"表不存在！");
                return;
            }
            //表存在
            FMResultSet *res = [db executeQuery:[self concatenateSQLStringWithType:SQLStringTypeGetAllData withModel:nil withDataCount:0]];
            while ([res next]) {
                NSString __block *jsonString = @"";
                /**
                {
                    JSON = "{\n  \"isAdult\" : true,\n  \"userAge\" : 99,\n  \"userName\" : \"\U5f20\U4e09\"\n}";
                    chatID = 1;
                }
                */
                [[res resultDictionary] enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
                    /**
                    res 中只有两个key  : json   chatID, 当key不等于chatID时，那就是json 开始获得JSON的字符串形式
                    */
                    if(![key isEqualToString:AUTOINCREMENT_FILEID]){
                        /**
                         数据库中存在的数据形式，数据库中存的不是 model 而是字典
                         {
                         "isAdult" : false,
                         "userAge" : 35,
                         "userName" : "鬼脚七"
                         }
                         */
                        jsonString = [NSString stringWithFormat:@"%@",obj];
                    }
                }];
                /**
                 将数据库中字典转成model
                 */
                id objc = [[[kclass class] alloc]init];
                objc = [self getModel:kclass withDataDic:[GDBDDatabaseUtils dictionaryWithJsonString:jsonString]];
                [result addObject:objc];
            }
        }
        [db close];
    }];
    return result;
}
/**
 修改修改单条数据
 */
- (BOOL)updateDataWithModel:(Class)kclass withFileName:(NSString *)fileName{
    
    return nil;
}

#pragma mark - 拼接SQL语句
- (NSString *)concatenateSQLStringWithType:(SQLStringType)type withModel:(id)model withDataCount:(NSInteger)count{
    //说明字符串
    NSString *statementString = @"";
    switch (type) {
            //创建表
        case SQLStringTypeCreate:{
            statementString = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ ('%@' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,JSON TEXT)",_dbTableName,AUTOINCREMENT_FILEID];
        }
            break;
            //插入数据
        case SQLStringTypeInsert:{
            NSString *jsonString = @"";
            NSMutableDictionary *jsonDic = [NSMutableDictionary dictionary];
            
            unsigned int numIvars;//成员变量个数
            NSString *kvarsKey = @"";//成员变量名称
            NSString *kvarsType = @"";//成员变量类型
            //成员变量名称数组
            NSMutableArray *kvarsKeyArray = [[NSMutableArray alloc]init];
            //成员变量类型数组
            NSMutableArray *kvarsTypeArray = [[NSMutableArray alloc]init];
            
            //获得model中成员变量
            Ivar *vars = class_copyIvarList([model class], &numIvars);
            //通过遍历获得变量的名称与类型
            for(int i = 0; i< numIvars; i++){
                Ivar thisVar = vars[i];
                //获得成员变量名称
                kvarsKey = [NSString stringWithUTF8String:ivar_getName(thisVar)];
                //去掉成员变量前的"_"
                if([kvarsKey hasPrefix:@"_"]){
                    kvarsKey = [kvarsKey stringByReplacingOccurrencesOfString:@"_" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)];
                    //此时不能去掉名称前的 '_'
//                    [kvarsKey substringFromIndex:1];
                }
                //获得成员变量的类型
                kvarsType = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisVar)];
                /**
                kvarsTypeArr
                <__NSArrayM 0x600003ee8ab0>(
                B,
                @"NSString",
                Q
                )

                (lldb) po kvarsKeyArr
                <__NSArrayM 0x600003ee8960>(
                isAdult,
                userName,
                userAge
                )
                */
                [kvarsKeyArray addObject:kvarsKey];
                [kvarsTypeArray addObject:kvarsType];
            }
            //拼接字典 key - 变量名称 value - 变量值
            [kvarsKeyArray enumerateObjectsUsingBlock:^(NSString *memberKey, NSUInteger idx, BOOL * _Nonnull stop) {
                id memberValue = [model valueForKey:memberKey] ? : @"";
                //@() 对id类型数据进行处理
                if([[kvarsTypeArray objectAtIndex:idx] isEqualToString:@"@"]){
                    memberValue = [GDBDDatabaseUtils setIDVariableToString:[model objectForKey:memberKey]];
                    //model 中 有集合类型
                }else if([[kvarsTypeArray objectAtIndex:idx] containsString:@"NSArray"] || [[kvarsTypeArray objectAtIndex:idx] containsString:@"NSMutableArray"] || [[kvarsTypeArray objectAtIndex:idx] containsString:@"NSDictionary"] ||[[kvarsTypeArray objectAtIndex:idx] containsString:@"NSMutableDictionary"]){
                    memberValue = [[NSJSONSerialization dataWithJSONObject:[model objectForKey:memberKey] options:NSJSONWritingPrettyPrinted error:0] base64EncodedStringWithOptions:0];
                }
                [jsonDic setObject:memberValue forKey:memberKey];
            }];
            //字典转JSON
            /**
             {
             "isAdult" : false,
             "userAge" : 39,
             "userName" : "王五"
             }
             */
            jsonString = [GDBDDatabaseUtils dictionaryToJson:jsonDic];
            //将需要json字符串转成sql语句
            /**
             INSERT INTO DBAtextDemoList (JSON) VALUES ('{
             "isAdult" : false,
             "userAge" : 81,
             "userName" : "周一仙"
             }')
             */
            statementString = [NSString stringWithFormat:@"INSERT INTO %@ (JSON) VALUES ('%@')",_dbTableName,jsonString];
            free(vars);
            
        }
            break;
        case SQLStringTypeUPdate:{
    
            NSString *jsonString = @"";
            NSMutableDictionary *jsonDic = [[NSMutableDictionary alloc]init];
            /**
             numIvars 没有负数
             */
            unsigned int numIvars;
            NSString *kvarsKey = @"";
            NSString *kvarsType = @"";
            
            NSMutableArray *kvarsKeyArr = [NSMutableArray array];
            NSMutableArray *kvarsTypeArr = [NSMutableArray  array];
            
            //需要根据model的其中的所有属性判断有多少键值对
            Ivar *vars =  class_copyIvarList([model class], &numIvars);
            
            for (int i = 0; i < numIvars; i ++) {
                Ivar thisIvar = vars[i];
                kvarsKey = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
                //去掉 “_”
                if([kvarsKey hasPrefix:@"_"]){
                    kvarsKey = [kvarsKey substringFromIndex:1];
                }
                //获得实例变量的类型
                kvarsType = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)];
                [kvarsKeyArr addObject:kvarsKey];
                [kvarsTypeArr addObject:kvarsType];
            }
            
            
            [kvarsKeyArr enumerateObjectsUsingBlock:^(NSString *memberKey, NSUInteger idx, BOOL * _Nonnull stop) {
                id memberValue = [model valueForKey:memberKey] ? : @"";
                if([[kvarsTypeArr objectAtIndex:idx] isEqualToString:@"@"]){
                    memberValue = [GDBDDatabaseUtils setIDVariableToString:[model valueForKey:memberKey]];
                }else if([[kvarsTypeArr objectAtIndex:idx] containsString:@"NSMutableArray"] || [[kvarsTypeArr objectAtIndex:idx] containsString:@"NSDictionary"] || [[kvarsTypeArr objectAtIndex:idx] containsString:@"NSMutableDictionary"]){
                    memberValue = [[NSJSONSerialization dataWithJSONObject:[model valueForKey:memberKey] options:NSJSONWritingPrettyPrinted error:nil] base64EncodedDataWithOptions:0];
                }
                
                //修改数据
                if([memberKey isEqualToString:@"title"]){
                    memberValue = @"你好";
                }else if([memberKey isEqualToString:@"name"]){
                    memberValue = @"科技";
                }else if([memberKey isEqualToString:@"desc"]){
                    memberValue = @"大炮";
                }else{
                    memberValue = @"dongdong!!";
                }
                
                [jsonDic setObject:memberValue forKey:memberKey];
                
            }];
            
            jsonString = [GDBDDatabaseUtils dictionaryToJson:jsonDic];
            
            //如果值为JSON 需要将JSON用''引起来
            statementString = [NSString stringWithFormat:@"UPDATE %@ SET JSON = '%@' WHERE chatID = %d",_dbTableName,jsonString,1];
        }
            break;
        case SQLStringTypeGetTheLastData:{
            
        }
            break;
        case SQLStringTypeGetSeaverlDate:{
            
        }
            break;
        case SQLStringTypeGetAllData:{
            
            statementString = [NSString stringWithFormat:@"SELECT * FROM %@",_dbTableName];
            
        }
            break;
        default:
            break;
    }
    return statementString;
}

#pragma mark - 通过字典获取模型数据
- (id)getModel:(Class)kClass withDataDic:(NSDictionary *)dic{
    id objc = [[[kClass class] alloc]init];
    unsigned int  methodCount = 0;
    NSString *kvarsKey = @""; //获取变量的名字
    NSString *kvarsType = @""; //获的变量类型
    
    //获得model中所有的变量
    Ivar *vars = class_copyIvarList([kClass class], &methodCount);
    //遍历model中所有的变量
    for (int i = 0; i < methodCount; i ++) {
        Ivar ivar = vars[i];
        //获取变量的名称
        kvarsKey = [NSString stringWithUTF8String:ivar_getName(ivar)];
        //去掉所有变量的前缀
        if([kvarsKey hasPrefix:@"_"]){
            kvarsKey = [kvarsKey substringFromIndex:1];
        }
        //获得变量的类型
        kvarsType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        //获得变量的值
        NSString *ivarValueString = [NSString stringWithFormat:@"%@",[dic objectForKey:kvarsKey]];
        //如果变量没有值，此次循环之后的操作就忽略掉
        if(!ivarValueString){continue;}
        //实例变量的值
        id ivarValue = ivarValueString;
        /**
         类型码判断，根据model中成员变量的类型 对变量进行赋值
         */
        // c - char
        if([kvarsType isEqualToString:@"c"]){
            ivarValue = [NSNumber numberWithChar:[ivarValueString intValue]];
            //i - int
        }else if([kvarsType isEqualToString:@"i"]){
            ivarValue = [NSNumber numberWithInt:[ivarValueString intValue]];
            // s- short
        }else if([kvarsType isEqualToString:@"s"]){
            ivarValue = [NSNumber numberWithShort:[ivarValueString intValue]];
            //l - long
        }else if([kvarsType isEqualToString:@"l"]){
            ivarValue =  [NSNumber numberWithLong:[ivarValueString intValue]];
            //q - long long
        }else if([kvarsType isEqualToString:@"q"]){
            ivarValue = [NSNumber numberWithLongLong:[ivarValueString intValue]];
            //C - unsigned char
        }else if([kvarsType isEqualToString:@"C"]){
            ivarValue =  [NSNumber numberWithUnsignedChar:[ivarValueString intValue]];
            //I - unsigned int
        }else if([kvarsType isEqualToString:@"I"]){
            ivarValue = [NSNumber numberWithUnsignedInt:[ivarValueString intValue]];
            //S - unsigned short
        }else if([kvarsType isEqualToString:@"S"]){
            ivarValue = [NSNumber numberWithUnsignedShort:[ivarValueString intValue]];
            //L - unsigned long
        }else if([kvarsType isEqualToString:@"L"]){
            ivarValue = [NSNumber numberWithUnsignedLong:[ivarValueString intValue]];
            //Q - unsigned long long
        }else if([kvarsType isEqualToString:@"Q"]){
            ivarValue = [NSNumber numberWithUnsignedLongLong:[ivarValueString intValue]];
            //f - float
        }else if([kvarsType isEqualToString:@"f"]){
            ivarValue = [NSNumber numberWithFloat:[ivarValueString floatValue]];
            //d - double
        }else if([kvarsType isEqualToString:@"d"]){
            ivarValue = [NSNumber numberWithDouble:[ivarValueString doubleValue]];
                    //v - void
            //        else if ([kvarsType isEqualToString:@"v"]) {}
                    //* - char *
            //        else if ([kvarsType isEqualToString:@"*"]) {}
            
            //B - bool or a C99 _BOOL
        }else if([kvarsType isEqualToString:@"B"]){
            // 这是基本的BOOL型数据
            if([ivarValueString isEqualToString:@"1"]){
                ivarValue = [NSNumber numberWithBool:YES];
            }else{
                ivarValue = [NSNumber numberWithBool:NO];
            }
            //@ - id
        }else if([kvarsType isEqualToString:@"@"]){
            //此处需要通过自定义的方法获得数据对象
            ivarValue = [GDBDDatabaseUtils getIDVariableValueTypesWithString:ivarValueString];
            //集合类型
        }else if([kvarsType isEqualToString:@"NSArray"] || [kvarsType isEqualToString:@"NSMutableArray"] || [kvarsType isEqualToString:@"NSDictionary"] || [kvarsType isEqualToString:@"NSMutableDictionary"]){
            
            ivarValue = [NSJSONSerialization JSONObjectWithData:[[NSData alloc] initWithBase64EncodedString:[dic objectForKey:kvarsKey] options:0] options:NSJSONReadingMutableLeaves error:nil];
            
            //未知类型
        }else{
            ivarValue = ivarValueString;
        }
        [objc setValue:ivarValue forKey:kvarsKey];
    }
    /**
     在使用了class_copyIvarList之后必须要 使用free
     */
    free(vars);
    return objc;
    
}

#pragma mark - 获取文件路径
- (void)getFilePathWithFileName:(NSString *)fileName{
    
    NSAssert(fileName || ![fileName isEqualToString:@""], @"数据库文件名不可以为空！！");
    //数据库表路径
    _dbTableName = fileName;
    //数据库路径
    _dbFilePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.db",_dbTableName]];
    
    NSLog(@"%@",_dbFilePath);
}

//- (FMDatabaseQueue *)databaseQueue{
//    if(_databaseQueue){
//        _databaseQueue = [FMDatabaseQueue databaseQueueWithPath:_dbFilePath];
//    }
//    return _databaseQueue;
//}

@end
